﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;
using StressTesterCommon;
using System.Xml.Linq;
using System.ComponentModel;
using Microsoft.SqlServer;


namespace StressServer
{
    public class StressClientProxy
    {
        #region Event declarations

        public delegate void TestAborted();
        public event TestAborted OnTestAborted;

        public delegate void ClientTestStarted();
        public event ClientTestStarted OnClientTestStarted;

        public delegate void TestConnectionFailed();
        public event TestConnectionFailed OnTestConnectionFailed;

        public delegate void TestConnectionOk();
        public event TestConnectionOk OnTestConnectionOk;

        public delegate void TestFailed();
        public event TestFailed OnTestconfigurationFailed;

        public delegate void TestOk();
        public event TestOk OnTestconfigurationOk;

        public delegate void Identified(StressClientProxy Client);
        public event Identified OnIdentified;

        public delegate void ClientMessageRecieved(Guid Id, TCPMessage Msg);
        public event ClientMessageRecieved MessageRecieved;

        public delegate void ClientQueryResultRecieved(StressClientProxy Client, QueryResult Result);
        public event ClientQueryResultRecieved QueryResultRecieved;

        public delegate void QueryResultSetRecieved(StressClientProxy Client, int Count);
        public event QueryResultSetRecieved OnQueryResultSetRecieved;

        public delegate void ClientDisconnect(StressClientProxy Client);
        public event ClientDisconnect OnDisconnect;

        public delegate void ClientTestCompleted(StressClientProxy Client);
        public event ClientTestCompleted OnClientTestCompleted;
        #endregion

        #region Members
        private TcpClient _Client;
        private bool _Disconnect = false;
        private Thread _ListenThread;
        private Guid _Id;
        private List<QueryResult> _QueryResults;
        private DateTime _LastHeartBeat;

        private int _QueriesCompleted = 0;

        public int QueriesCompleted
        {
            get { return _QueriesCompleted; }
            set { _QueriesCompleted = value; }
        }

        private int _QueryErrors = 0;
        public int QueryErrors
        {
            get { return _QueryErrors; }
            set { _QueryErrors = value; }
        }

        public DateTime LastHeartBeat
        {
            get { return _LastHeartBeat; }
            set { _LastHeartBeat = value; }
        }

        public int SecondsSinceLastContact
        {
            get
            {
                TimeSpan T = DateTime.Now - _LastHeartBeat;
                return T.Seconds;
            }
        }

        private StressServer _Parent;

        public StressServer Parent
        {
            get { return _Parent; }
            set { _Parent = value; }
        }

        public List<QueryResult> QueryResults
        {
            get { return _QueryResults; }
            set { _QueryResults = value; }
        }

        public Guid Id
        {
            get { return _Id; }
            set { _Id = value; }
        }


        public string IP
        {
            get { return ((IPEndPoint)_Client.Client.RemoteEndPoint).Address.ToString(); }
        }
        public TcpClient Client
        {
            get { return _Client; }
            set
            {
                _Client = value;

            }
        }
        private string _MachineName;

        public string MachineName
        {
            get { return _MachineName; }
            set { _MachineName = value; }
        }
        private string _DomainName;

        public string DomainName
        {
            get { return _DomainName; }
            set { _DomainName = value; }
        }
        private string _UserName;

        public string UserName
        {
            get { return _UserName; }
            set { _UserName = value; }
        }
        #endregion
        public StressClientProxy()
        {
            _Id = Guid.NewGuid();
            _LastHeartBeat = DateTime.Now;
        }

        public void AbortTest()
        {
            SendMsg(MessageTypes.ClientAbortTest, "");
        }

        private void SendMsg(MessageTypes Type, string Message)
        {
            if (_Client != null && _Client.Connected)
            {
                try
                {
                    byte[] Msg = TCPMessage.FormatMessage(Type, Message);
                    NetworkStream s = _Client.GetStream();
                    s.Write(Msg, 0, Msg.Length);
                }
                catch
                {
                    throw;
                }
            }

        }

        public void SendExecuteTest()
        {
            this.QueryErrors = 0;
            this.QueriesCompleted = 0;
            _QueryResults = new List<QueryResult>();
            SendMsg(MessageTypes.ExecuteTest, "");
        }

        public void SendTestConfiguration(Test Test)
        {
            SendMsg(MessageTypes.TestConfiguration, Test.ToXDocument().ToString());
        }

        public void SendTestConnection()
        {
            SendMsg(MessageTypes.TestConnection, "");
        }
        public void SendMessage(string Message)
        {
            SendMsg(MessageTypes.ServerMessage, Message);
        }

        public void SendHeartBeat()
        {
            SendMsg(MessageTypes.HeartBeat, "");
        }

        public void Listen()
        {
            _ListenThread = new Thread(new ThreadStart(Listen2));
            _ListenThread.SetApartmentState(ApartmentState.STA);
            _ListenThread.Start();
        }

        public void Listen2()
        {
            NetworkStream Read = _Client.GetStream();

            while (!_Disconnect && Read != null)
            {
                if (Read.DataAvailable)
                {
                    StressTesterCommon.TCPMessage Msg = TCPReader.ReadMessageFromStream(Read);
                    MessageRecieved(_Id, Msg);
                    HandleMessage(Msg);
                }
                Thread.Sleep(10);
            }
            _Client.Close();
        }


        private void LoadClientData(string ClientData)
        {
            try
            {
                XElement Elem = XElement.Parse(ClientData);
                MachineName = Elem.Element("machinename").Value;
                DomainName = Elem.Element("domainname").Value;
                UserName = Elem.Element("username").Value;

            }
            catch (Exception e)
            {
                throw;
            }
            //Fire event associated with this
            OnIdentified(this);
        }

        private void HandleQueryResultMessage(TCPMessage Message)
        {
            QueryResult Res = new QueryResult();
            Res.LoadFromXMLString(Message.Message);
            if (Res.ExecuteStatus == QueryExecuteStatus.QueryFailed)
            {
                this.QueryErrors++;
            }
            this.QueriesCompleted++;
            QueryResults.Add(Res);
            QueryResultRecieved(this, Res);
        }
        private void HandleQueryResultSetMessage(TCPMessage Message)
        {
            XDocument Set = XDocument.Parse(Message.Message);
            int i = 0;
            foreach(XElement Result in Set.Element("queryresults").Elements("queryresult")) 
            {
                i++;
                QueryResult Res = new QueryResult();
                Res.LoadFromXMLString(Result.ToString());
                Res.ClientId = this.Id;
                if (Res.ExecuteStatus == QueryExecuteStatus.QueryFailed)
                {
                    this.QueryErrors++;
                }
                QueryResults.Add(Res);
            }
            if (OnQueryResultSetRecieved != null)
            {
                OnQueryResultSetRecieved(this,i);
            }
            this.QueriesCompleted+= i;
        }
        private void HandleClientTestStarted()
        {
            if (OnClientTestStarted != null)
            {
                OnClientTestStarted();
            }
        }
        private void HandleHeartBeat()
        {
            _LastHeartBeat = DateTime.Now;
        }
        private void HandleTestConfigurationOk()
        {
            if (OnTestconfigurationOk != null)
            {
                OnTestconfigurationOk();
            }
        }

        private void HandleTestConfigurationFailed()
        {
            if (OnTestconfigurationFailed != null)
            {
                OnTestconfigurationFailed();
            }
        }
        private void HandleTestConnectionFailed()
        {
            if (OnTestConnectionFailed != null)
            {
                OnTestConnectionFailed();
            }
        }

        private void HandleTestConnectionOk()
        {
            if (OnTestConnectionOk != null)
            {
                OnTestConnectionOk();
            }
        }

        private void HandleDisconnect()
        {
            if (OnDisconnect != null) OnDisconnect(this);
            _Disconnect = true;

        }

        private void HandleAbortTest()
        {
            if (OnTestAborted != null) OnTestAborted();
        }
        private void HandleMessage(TCPMessage Message)
        {
            _LastHeartBeat = DateTime.Now;
            switch (Message.MessageType)
            {
                case (MessageTypes.ClientProvideIdData):
                    LoadClientData(Message.Message);
                    break;
                case (MessageTypes.QueryResult):
                    HandleQueryResultMessage(Message);
                    break;
                case (MessageTypes.QueryResultSet):
                    HandleQueryResultSetMessage(Message);
                    break;
                case MessageTypes.ClientDisconnect:
                    HandleDisconnect();                
                    break;
                case MessageTypes.TestCompleted:
                    if (OnClientTestCompleted != null)
                    {
                        OnClientTestCompleted(this);
                    }
                    break;
                case MessageTypes.HeartBeat:
                    HandleHeartBeat();
                    break;
                case MessageTypes.TestConfigurationOk:
                    HandleTestConfigurationOk();
                    break;
                case MessageTypes.TestConfigurationFailed:
                    HandleTestConfigurationFailed();
                    break;
                case MessageTypes.TestConnectionOk:
                    HandleTestConnectionOk();
                    break;
                case MessageTypes.TestConnectionFailed:
                    HandleTestConnectionFailed();
                    break;
                case MessageTypes.ClientTestStarted:
                    HandleClientTestStarted();
                    break;
                case MessageTypes.ClientAbortTest:
                    HandleAbortTest();
                    break;
                default:
                    break;
            }
        }

        public void Disconnect()
        {
            //Signal to the client that it should disconnect
            SendMsg(MessageTypes.ClientDisconnect, "");
        }

    }
}
